/**
 * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 */
package com.liferay.analystest.controller;

import com.liferay.analystest.model.Sample;
import com.liferay.analystest.model.XYItem;
import com.liferay.analystest.util.*;
import com.liferay.analystest.window.ChartPane;
import java.awt.Color;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import org.jfree.chart.event.ChartProgressEvent;
import org.jfree.chart.event.ChartProgressListener;

import org.jfree.data.xy.XYSeries;

/**
 * @author Dante Wang
 */
public class ChartPaneController implements ChartProgressListener{

	public ChartPaneController(Path basePath) {
		_basePath = Objects.requireNonNull(basePath);

		_sessionSeries = new XYSeries("Session");

		CHART = new ChartPane(_sessionSeries);

		init();
	}

	@Override
	public void chartProgress(ChartProgressEvent cpe) {
		if (cpe.getType() == ChartProgressEvent.DRAWING_FINISHED) {
			_selectedItem = null;

			long x =
				(long)CHART.getXYPlot().getDomainCrosshairValue();
			long y =
				(long)CHART.getXYPlot().getRangeCrosshairValue();

			@SuppressWarnings("unchecked")
			List<XYItem> items = _sessionSeries.getItems();

			for (XYItem item : items) {
				if (item.getSample().getRevision() == x &&
						item.getSample().getSession() == y) {

					_selectedItem = item;
				}
			}

			if (_selectedItem != null) {
				CHART.showInfoText(_selectedItem.getSample().toString());
			}
		}
	}

	@SuppressWarnings("unchecked")
	public void update() throws Exception {
		List<Path> oldFileList = _fileList;

		_fileList = IndexUtil.doIndex(_basePath);

		for (Path p : _fileList) {
			// this is a new file : only exists in new list
			// should be added to XYSeries
			if (!oldFileList.contains(p)) {
				Sample sample = parseFile(p);

				XYItem item = new XYItem(
					sample.getRevision(), sample.getSession(), sample);

				_sessionSeries.addOrUpdate(item);
			}
		}

		for (Path p : oldFileList) {
			// this is a removed file : only exists in old list
			if (!_fileList.contains(p)) {
				String name = parseName(p);

				removeItem(name);
			}
		}
	}

	private Path getPathFromSample(Sample sample) {
		String revTimestamp = sample.getName();
		String filename = "log-".concat(revTimestamp).concat(".zip");

		return _basePath.resolve(filename);
	}

	private void init() {
		CHART.setSeriesColor(_sessionSeries, Color.blue);

		CHART.addProgressListener(this);
	}

	private String parseName(Path file) {
		String fileName = file.toFile().getName();

		String revision = ParseUtil.parseR("log-(\\d+)-", fileName);
		String timestamp = ParseUtil.parseR("log-\\d+-(.+)\\.", fileName);

		return revision.concat("-").concat(timestamp);
	}

	private Sample parseFile(Path file) throws Exception {
		String fileName = file.toFile().getName();
		HashMap<String, String> r = null;

		// Get revision number from filename
		String revision = ParseUtil.parseR("log-(\\d+)-", fileName);
		String timestamp = ParseUtil.parseR("log-\\d+-(.+)\\.", fileName);

		// parse benchmark-ext.properties
		List<String> benchmarkExtRows =
			ZipUtil.extractLines(
				"/grinder/configs/benchmark-ext.properties", file);
		r = parseBenchmarkExt(benchmarkExtRows);

		String testcase = r.get("testcase");
		String databaseName = r.get("database");

		// Parse grinder.log
		String grinderLog = ZipUtil.extract("/grinder/logs/grinder.log", file);

		r = parseGrinderLog(grinderLog);

		String session = r.get("session");
		String meantime = r.get("meantime");

		// construct sample object
		Sample sample = new Sample(revision, session, timestamp);

		sample.addAdditionalInformation("Meantime", meantime);
		sample.addAdditionalInformation("Testcase", testcase);
		sample.addAdditionalInformation("Database", databaseName);

		return sample;
	}

	/*
	 * Need to parse:
	 * 1. database name [OK]
	 * 2. test case name [OK]
	 * 3. grinder parameters
agent.new.thread.model.init.permits=10000
	 */
	private HashMap<String, String> parseBenchmarkExt(List<String> rows) {
		HashMap<String, String> r = null;
		//int lineCount = rows.size();

		if (rows != null) {
			String database =
				ParseUtil.parseS("database.name=", rows);

			String testcase =
				ParseUtil.parseS("grinder.script=script/",
					Const.TEST_SCRIPT_EXTENSION, rows);
			testcase = testcase.toLowerCase();

			r = new HashMap<>();

			r.put("testcase", testcase);

			r.put("database", database);
		}

		return r;
	}

	private HashMap<String, String> parseGrinderLog(String content)
		throws Exception {

		HashMap<String, String> r = null;

		String sessionPattern = "Average\\s+\\d+\\s+(\\d+)";
		String meantimePattern = "Total\\s+\\d+\\s+\\d+\\s+(.+?)\\s";

		String session =
			Objects.requireNonNull(
				ParseUtil.parseR(sessionPattern, content, -1));

		String meantime =
			ParseUtil.parseR(meantimePattern, content, -1);

		if (meantime == null) {
			meantime = "0";
		}

		r = new HashMap<>();

		r.put("session", session);
		r.put("meantime", meantime);

		return r;
	}

	/*
	 * Need to parse:
	 * 1. full GC times
	 * 2. first parnew number after last recorded full gc
	 * 3. promotion failure
	 */
	private HashMap<String, String> parsePortalGCLog() {
		return null;
	}

	private void removeItem(String name) {
		@SuppressWarnings("unchecked")
		List<XYItem> items = _sessionSeries.getItems();

		for (int i = 0; i < items.size(); i++) {
			if (items.get(i).getSample().getName().equals(name)) {
				_sessionSeries.remove(i);
			}
		}
	}

	// variables
	public final ChartPane CHART;

	private final Path _basePath;

	private List<Path> _fileList = new ArrayList<>();
	private XYItem _selectedItem;
	private XYSeries _sessionSeries;	

}
